// Copyright (c) 2021-2025 ByteDance Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

// shadowhook is now credited to all its contributors.
//
// Before shadowhook was open sourced in February 2022, it was developed by
// the following developers from ByteDance's App Health Team:
//
// Kelun Cai (caikelun@bytedance.com)
// Pengying Xu (xupengying@bytedance.com)
//

#ifndef BYTEDANCE_SHADOWHOOK_H
#define BYTEDANCE_SHADOWHOOK_H

#include <link.h>
#include <stdbool.h>
#include <stdint.h>

#define SHADOWHOOK_VERSION "2.0.0"

#define SHADOWHOOK_ERRNO_OK                     0
#define SHADOWHOOK_ERRNO_PENDING                1
#define SHADOWHOOK_ERRNO_UNINIT                 2
#define SHADOWHOOK_ERRNO_INVALID_ARG            3
#define SHADOWHOOK_ERRNO_OOM                    4
#define SHADOWHOOK_ERRNO_MPROT                  5
#define SHADOWHOOK_ERRNO_WRITE_CRASH            6
#define SHADOWHOOK_ERRNO_INIT_ERRNO             7
#define SHADOWHOOK_ERRNO_INIT_SIGSEGV           8
#define SHADOWHOOK_ERRNO_INIT_SIGBUS            9
#define SHADOWHOOK_ERRNO_INTERCEPT_DUP          10
#define SHADOWHOOK_ERRNO_INIT_SAFE              11
#define SHADOWHOOK_ERRNO_INIT_LINKER            12
#define SHADOWHOOK_ERRNO_INIT_HUB               13
#define SHADOWHOOK_ERRNO_HUB_CREAT              14
#define SHADOWHOOK_ERRNO_MONITOR_DLOPEN         15
#define SHADOWHOOK_ERRNO_HOOK_HUB_DUP           16
#define SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH      17
#define SHADOWHOOK_ERRNO_HOOK_DLSYM             18
#define SHADOWHOOK_ERRNO_HOOK_DLSYM_CRASH       19
#define SHADOWHOOK_ERRNO_HOOK_DUP               20
#define SHADOWHOOK_ERRNO_HOOK_DLADDR_CRASH      21
#define SHADOWHOOK_ERRNO_HOOK_DLINFO            22
#define SHADOWHOOK_ERRNO_HOOK_SYMSZ             23
#define SHADOWHOOK_ERRNO_HOOK_ENTER             24
#define SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH     25
#define SHADOWHOOK_ERRNO_HOOK_REWRITE_FAILED    26
#define SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND        27
#define SHADOWHOOK_ERRNO_UNHOOK_CMP_CRASH       28
#define SHADOWHOOK_ERRNO_UNHOOK_TRAMPO_MISMATCH 29
#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH   30
#define SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH      31
#define SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR        32
#define SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED   33
#define SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH      34
#define SHADOWHOOK_ERRNO_LINKER_ARCH_MISMATCH   35
#define SHADOWHOOK_ERRNO_DUP                    36
#define SHADOWHOOK_ERRNO_NOT_FOUND              37
#define SHADOWHOOK_ERRNO_NOT_SUPPORT            38
#define SHADOWHOOK_ERRNO_INIT_TASK              39
#define SHADOWHOOK_ERRNO_HOOK_ISLAND_EXIT       40
#define SHADOWHOOK_ERRNO_HOOK_ISLAND_ENTER      41
#define SHADOWHOOK_ERRNO_HOOK_ISLAND_REWRITE    42
#define SHADOWHOOK_ERRNO_MODE_CONFLICT          43
#define SHADOWHOOK_ERRNO_HOOK_MULTI_DUP         44
#define SHADOWHOOK_ERRNO_DISABLED               45

#ifdef __cplusplus
extern "C" {
#endif

const char *shadowhook_get_version(void);

// init
typedef enum {
  // The same address can be hooked multiple times (with proxies management, avoid recursion)
  SHADOWHOOK_MODE_SHARED = 0,
  // The same address can only be hooked once
  SHADOWHOOK_MODE_UNIQUE = 1,
  // The same address can be hooked multiple times
  SHADOWHOOK_MODE_MULTI = 2
} shadowhook_mode_t;
int shadowhook_init(shadowhook_mode_t default_mode, bool debuggable);
int shadowhook_get_init_errno(void);

// get and set attributes
#define SHADOWHOOK_IS_SHARED_MODE (SHADOWHOOK_MODE_SHARED == shadowhook_get_mode())
#define SHADOWHOOK_IS_UNIQUE_MODE (SHADOWHOOK_MODE_UNIQUE == shadowhook_get_mode())
#define SHADOWHOOK_IS_MULTI_MODE  (SHADOWHOOK_MODE_MULTI == shadowhook_get_mode())
shadowhook_mode_t shadowhook_get_mode(void);
bool shadowhook_get_debuggable(void);
void shadowhook_set_debuggable(bool debuggable);
bool shadowhook_get_recordable(void);
void shadowhook_set_recordable(bool recordable);
bool shadowhook_get_disable(void);
void shadowhook_set_disable(bool disable);

// get error-number and error message
int shadowhook_get_errno(void);
const char *shadowhook_to_errmsg(int error_number);

// hook and unhook
typedef void (*shadowhook_hooked_t)(int error_number, const char *lib_name, const char *sym_name,
                                    void *sym_addr, void *new_addr, void *orig_addr, void *arg);
void *shadowhook_hook_func_addr(void *func_addr, void *new_addr, void **orig_addr);
void *shadowhook_hook_sym_addr(void *sym_addr, void *new_addr, void **orig_addr);
void *shadowhook_hook_sym_name(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr);
void *shadowhook_hook_sym_name_callback(const char *lib_name, const char *sym_name, void *new_addr,
                                        void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg);
int shadowhook_unhook(void *stub);

// hook with flags
#define SHADOWHOOK_HOOK_DEFAULT          0  // 0b0000
#define SHADOWHOOK_HOOK_WITH_SHARED_MODE 1  // 0b0001
#define SHADOWHOOK_HOOK_WITH_UNIQUE_MODE 2  // 0b0010
#define SHADOWHOOK_HOOK_WITH_MULTI_MODE  4  // 0b0100
#define SHADOWHOOK_HOOK_RECORD           8  // 0b1000
void *shadowhook_hook_func_addr_2(void *func_addr, void *new_addr, void **orig_addr, uint32_t flags,
                                  ... /* char *record_lib_name, char *record_sym_name */);
void *shadowhook_hook_sym_addr_2(void *sym_addr, void *new_addr, void **orig_addr, uint32_t flags,
                                 ... /* char *record_lib_name, char *record_sym_name */);
void *shadowhook_hook_sym_name_2(const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr,
                                 uint32_t flags);
void *shadowhook_hook_sym_name_callback_2(const char *lib_name, const char *sym_name, void *new_addr,
                                          void **orig_addr, uint32_t flags, shadowhook_hooked_t hooked,
                                          void *hooked_arg);

// intercept and unintercept
typedef union {
#if defined(__aarch64__)
  __uint128_t q;
#endif
  uint64_t d[2];
  uint32_t s[4];
  uint16_t h[8];
  uint8_t b[16];
} shadowhook_vreg_t;

#if defined(__aarch64__)
typedef struct {
  uint64_t regs[31];  // x0-x30
  uint64_t sp;
  uint64_t pc;
  uint64_t pstate;
  shadowhook_vreg_t vregs[32];  // q0-q31
  uint64_t fpsr;
  uint64_t fpcr;
} shadowhook_cpu_context_t;
#elif defined(__arm__)
typedef struct {
  uint32_t regs[16];  // r0-r15
  uint32_t cpsr;
  uint32_t fpscr;
  // (1) NEON, VFPv4, VFPv3-D32: q0-q15(d0-d31)
  // (2) VFPv3-D16: q0-q7(d0-d15)
  shadowhook_vreg_t vregs[16];
} shadowhook_cpu_context_t;
#endif

#define SHADOWHOOK_INTERCEPT_DEFAULT                0  // 0b000
#define SHADOWHOOK_INTERCEPT_WITH_FPSIMD_READ_ONLY  1  // 0b001
#define SHADOWHOOK_INTERCEPT_WITH_FPSIMD_WRITE_ONLY 2  // 0b010
#define SHADOWHOOK_INTERCEPT_WITH_FPSIMD_READ_WRITE 3  // 0b011
#define SHADOWHOOK_INTERCEPT_RECORD                 4  // 0b100
typedef void (*shadowhook_interceptor_t)(shadowhook_cpu_context_t *cpu_context, void *data);
typedef void (*shadowhook_intercepted_t)(int error_number, const char *lib_name, const char *sym_name,
                                         void *sym_addr, shadowhook_interceptor_t pre, void *data, void *arg);
void *shadowhook_intercept_instr_addr(void *instr_addr, shadowhook_interceptor_t pre, void *data,
                                      uint32_t flags, ... /* char *record_lib_name, char *record_sym_name */);
void *shadowhook_intercept_func_addr(void *func_addr, shadowhook_interceptor_t pre, void *data,
                                     uint32_t flags, ... /* char *record_lib_name, char *record_sym_name */);
void *shadowhook_intercept_sym_addr(void *sym_addr, shadowhook_interceptor_t pre, void *data, uint32_t flags,
                                    ... /* char *record_lib_name, char *record_sym_name */);
void *shadowhook_intercept_sym_name(const char *lib_name, const char *sym_name, shadowhook_interceptor_t pre,
                                    void *data, uint32_t flags);
void *shadowhook_intercept_sym_name_callback(const char *lib_name, const char *sym_name,
                                             shadowhook_interceptor_t pre, void *data, uint32_t flags,
                                             shadowhook_intercepted_t intercepted, void *intercepted_arg);
int shadowhook_unintercept(void *stub);

// get operation records
#define SHADOWHOOK_RECORD_ITEM_ALL             0x7FF  // 0b11111111111
#define SHADOWHOOK_RECORD_ITEM_TIMESTAMP       (1 << 0)
#define SHADOWHOOK_RECORD_ITEM_CALLER_LIB_NAME (1 << 1)
#define SHADOWHOOK_RECORD_ITEM_OP              (1 << 2)
#define SHADOWHOOK_RECORD_ITEM_LIB_NAME        (1 << 3)
#define SHADOWHOOK_RECORD_ITEM_SYM_NAME        (1 << 4)
#define SHADOWHOOK_RECORD_ITEM_SYM_ADDR        (1 << 5)
#define SHADOWHOOK_RECORD_ITEM_NEW_ADDR        (1 << 6)
#define SHADOWHOOK_RECORD_ITEM_BACKUP_LEN      (1 << 7)
#define SHADOWHOOK_RECORD_ITEM_ERRNO           (1 << 8)
#define SHADOWHOOK_RECORD_ITEM_STUB            (1 << 9)
#define SHADOWHOOK_RECORD_ITEM_FLAGS           (1 << 10)
char *shadowhook_get_records(uint32_t item_flags);
void shadowhook_dump_records(int fd, uint32_t item_flags);

// helper functions for "get symbol-address from library-name and symbol-name"
void *shadowhook_dlopen(const char *lib_name);
void shadowhook_dlclose(void *handle);
void *shadowhook_dlsym(void *handle, const char *sym_name);
void *shadowhook_dlsym_dynsym(void *handle, const char *sym_name);
void *shadowhook_dlsym_symtab(void *handle, const char *sym_name);

// register and unregister callbacks for executing dynamic library's .init .init_array / .fini .fini_array
typedef void (*shadowhook_dl_info_t)(struct dl_phdr_info *info, size_t size, void *data);
int shadowhook_register_dl_init_callback(shadowhook_dl_info_t pre, shadowhook_dl_info_t post, void *data);
int shadowhook_unregister_dl_init_callback(shadowhook_dl_info_t pre, shadowhook_dl_info_t post, void *data);
int shadowhook_register_dl_fini_callback(shadowhook_dl_info_t pre, shadowhook_dl_info_t post, void *data);
int shadowhook_unregister_dl_fini_callback(shadowhook_dl_info_t pre, shadowhook_dl_info_t post, void *data);

// for internal use
void *shadowhook_get_prev_func(void *func);
void shadowhook_pop_stack(void *return_address);
void shadowhook_allow_reentrant(void *return_address);
void shadowhook_disallow_reentrant(void *return_address);
void *shadowhook_get_return_address(void);

#ifdef __cplusplus
}
#endif

// call previous function in proxy-function
#ifdef __cplusplus
#define SHADOWHOOK_CALL_PREV(func, ...) \
  ((decltype(&(func)))shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__)
#else
#define SHADOWHOOK_CALL_PREV(func, func_sig, ...) \
  ((func_sig)shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__)
#endif

// pop stack in proxy-function (for C/C++)
#define SHADOWHOOK_POP_STACK() shadowhook_pop_stack(__builtin_return_address(0))

// pop stack in proxy-function (for C++ only)
#ifdef __cplusplus
class ShadowhookStackScope {
 public:
  ShadowhookStackScope(void *return_address) : return_address_(return_address) {}
  ~ShadowhookStackScope() {
    shadowhook_pop_stack(return_address_);
  }

 private:
  void *return_address_;
};
#define SHADOWHOOK_STACK_SCOPE() ShadowhookStackScope shadowhook_stack_scope_obj(__builtin_return_address(0))
#endif

// allow reentrant of the current proxy-function
#define SHADOWHOOK_ALLOW_REENTRANT() shadowhook_allow_reentrant(__builtin_return_address(0))

// disallow reentrant of the current proxy-function
#define SHADOWHOOK_DISALLOW_REENTRANT() shadowhook_disallow_reentrant(__builtin_return_address(0))

// get return address in proxy-function
#define SHADOWHOOK_RETURN_ADDRESS() shadowhook_get_return_address()

#endif
